#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include "aimage.h"
#include "aimage_utils.h"
#include "aimage_dcraw.h"
#include "argv_parse.h"
#include "aimage_ppm.h"

//#define DUMPSTARS
//#define THRESH .3
//#define THRESH .20

float scale_red = 1.0f;
float scale_blue = 1.0f;
float score_green = 1.0f;
float threshold = 0.20f;
float congestion_weight = 1.0f;
float cull_ratio = .55f;
float distortion_ratio = .4f;

static ARG_FLOATLIST fargs[]=
{
	{ "-gr", &scale_red },
	{ "-gb", &scale_blue },
	{ "-gg", &score_green },
	{ "-threshold", &threshold },
	{ "-congestion_weight", &threshold },
	{ "-cull_ratio", &cull_ratio },
	{ "-distortion_ratio", &distortion_ratio },
};

#define NUM_FARGS ( sizeof( fargs ) / sizeof( ARG_FLOATLIST ))

int main( int argc, const char *argv[] )
{
	AIMAGE image;
	ASTAR_FIELD orig_stars;
	int i;
	AIMAGE orig_image;
	int width, height;
	ASTAR_TRANSFORM t;
//	float congestion_weight = 20.0;
	int carg = 1;

	while ( ARG_PARSER::parse_float_args( argv, carg, fargs, NUM_FARGS )); 
	printf("%s\n", argv[carg] );


	const rlim_t kStackSize = 64L * 1024L * 1024L;
	struct rlimit rl;

	int result;
	result = getrlimit( RLIMIT_STACK, &rl );
	if ( result == 0 )
	{
		fprintf( stderr, "Stack size is %dK\n", ((int) ( rl.rlim_cur / 1024L )));
		if ( rl.rlim_cur < kStackSize )
		{
			rl.rlim_cur = kStackSize;
			result = setrlimit( RLIMIT_STACK, &rl );
			if ( result == 0 )
			{
				fprintf( stderr, "Stack size set to %dK\n", ((int) (rl.rlim_cur / 1024L )));
			}
		}
	}

	setrlimit( RLIMIT_STACK, &rl );

	if ( argc <= 2 )
	{
		fprintf(stderr, "%s file1 file2...\n", argv[0] );
		exit( 1 );
	}

	AIMAGE_DCRAW_UTILS::load( image, argv[ carg ] );
	AIU::gamma( image, scale_red, score_green, scale_blue ); 
	image.get_dim( width, height );
	orig_image.alloc_image( width, height );
	AIU::cls( orig_image, FULL_INT, 0, 0 );
	orig_image.copy( image, 0, 0, width, height, 0, 0 );

	image.starfind( threshold, orig_stars );
	fprintf(stderr, "Stars %d\n", orig_stars.get_num_stars());

	AIMAGE cong_map;
	cong_map.make_congestion_map( image, orig_stars, true );
	orig_stars.filter_by_distortion( distortion_ratio );
	orig_stars.compute_goodness( cong_map, congestion_weight );
	orig_stars.top_x_by_goodness( 20 );
	cong_map.make_congestion_map( image, orig_stars, false);
	AIMAGE_PPM_UTILS::save( cong_map, "congestion.ppm", false );
	orig_stars.compute_goodness( cong_map, congestion_weight );
	orig_stars.top_x_by_goodness( 10 );

	//orig_image.drawstars( orig_stars ); 
	//orig_image.writeppm( "stdout", false );
	//exit( 0 );


	//orig_stars.filter_by_distortion( .5 );

	for ( i = carg+1; i < argc; ++i )
	{
		FILE *f;
		ASTAR_FIELD stars;

		if ( strcmp( argv[i], "-xhint") == 0 )
		{
			i++;
			t.xo = atof(argv[i]);
			continue;
		}
		if ( strcmp( argv[i], "-yhint") == 0 )
		{
			i++;
			t.yo = atof(argv[i]);
			continue;
		}

		f = fopen( argv[ i ], "rb" );
		fseek( f, 0, SEEK_END );
		long pos = ftell( f );
		fclose( f );

		if ( pos < 1024 )
		{
			continue;
		}
		fprintf(stderr, "File %s\n", argv[ i ] );

		AIMAGE_DCRAW_UTILS::load( image, argv[ i ] );
//#define DUMPSTARS
#ifdef DUMPSTARS
		AIMAGE pimage;
		AIMAGE_DCRAW_UTILS::load( pimage, argv[ i ] );
		AIU::gamma( pimage, scale_red, score_green, scale_blue ); 
#endif
		//AIU::colour_scale( image, scale_red, score_green, scale_blue ); 
		AIU::gamma( image, scale_red, score_green, scale_blue ); 
		image.starfind( threshold, stars );
		fprintf(stderr, "  Stars before distortion filter %d\n", stars.get_num_stars());
		stars.filter_by_distortion( distortion_ratio );
		fprintf(stderr, "  Stars %d\n", stars.get_num_stars());
		if ( stars.get_num_stars() < 20 )
		{
			fprintf(stderr, "  Rejected on star count\n" );
			continue;
		}
		stars.compute_goodness( cong_map, congestion_weight );
		stars.top_x_by_goodness( 20 );

		t = orig_stars.synch_fields( stars, t );

		float d_of_record;
		d_of_record = orig_stars.avg_distance( stars, t ); 

		ASTAR_FIELD transform_stars;

		transform_stars.transform( stars, t );
 
		transform_stars.reverse_map( orig_stars );
		transform_stars.filter_out_unmapped();

		stars.reverse_map( orig_stars );
#ifndef DUMPSTARS
		stars.filter_out_unmapped();
#endif

#ifdef DUMPSTARS
		APIXEL white, grey, blue;
		white.r = 0; white.g = FULL_INT; white.b = 0;
		grey.r = FULL_INT; grey.g = 0; grey.b = 0;
		blue.r = 0; blue.g = 0; blue.b = FULL_INT;
		orig_image.drawstars( orig_stars, white ); 
		pimage.drawstars( orig_stars, white ); 
		//orig_image.drawstars( transform_stars, grey ); 
		orig_image.drawstars( stars, grey ); 
		pimage.drawstars( stars, grey ); 

		int istar;

		orig_stars.map_to( transform_stars );

		for ( istar = 0; istar < orig_stars.get_num_stars(); ++istar )
		{
			int iostar = orig_stars.mapping_function[ istar ];

			float x0, x1, y0, y1;

			x0 = orig_stars.get_star( istar ).xc;
			y0 = orig_stars.get_star( istar ).yc;
			x1 = transform_stars.get_star( iostar ).xc;
			y1 = transform_stars.get_star( iostar ).yc;

			orig_image.line( x0, y0, x1, y1, blue );

			fprintf( stderr, "%d -> %d (%g %g) to (%g %g)\n", istar, iostar, x0, y0, x1, y1 ); 
		}

		fprintf(stderr, "writing image\n");
		//AIMAGE_PPM_UTILS::save( orig_image, "dumpstars.ppm", false );
		AIMAGE_PPM_UTILS::save( pimage, "dumpstars.ppm", false );

		exit( 0 );
#endif

#ifdef TRIP
		stars.map_to( orig_stars );
		stars.filter_out_unmapped();
#endif

		fprintf(stderr, "%d stars found\n", stars.get_num_stars());
		float myscore = stars.get_score();
		float myratio = stars.get_ratio();

		fprintf(stderr, "Score %9.2f File %s Ratio %g Score %g Distance %9.2f Angle %g XTrans %g YTrans %g\n", 
			myscore, argv[ i ], myratio, myscore, d_of_record,
			t.angle, t.xo, t.yo );

		if ( myratio >= cull_ratio )
		{
			printf("Score %9.2f File %s Ratio %g Score %g Distance %9.2f Angle %g XTrans %g YTrans %g\n", 
				myscore, argv[ i ], myratio, myscore, d_of_record,
				t.angle, t.xo, t.yo );
		}
		else
		{
			fprintf(stderr,"   Culled becuase ratio < %g\n", cull_ratio );
		}
	}

#ifdef GONE
	if ( argc == 2 )
	{
		fprintf(stderr, "reading image %s\n", argv[1] );
		image.readppm( argv[1] );
	}
	else
	{
		image.readppm( "stdin" );
	}

	fprintf(stderr, "copy image\n");
	image.get_dim( width, height );
	image2.alloc_image( width, height );
	image2.cls( FULL_INT, 0, 0 );

	ASTAR_FIELD orig_stars;
	fprintf(stderr, "   copy image\n");
	image2.copy( image, 0, 0, width, height, 0, 0 );
	fprintf(stderr, "   find stars\n");
	image2.starfind( .20, orig_stars );
	fprintf(stderr, "   filter by distortion\n");
	orig_stars.filter_by_distortion( .5 );
	fprintf(stderr, "   filter to pixels\n");
	orig_stars.top_x_by_pixels( 20 );
	best_sample = orig_stars.get_ratio();
	ASTAR_FIELD best_stars;
	best_stars = orig_stars;
	fprintf(stderr, "Orig sample %g stars %d\n", best_sample,
		orig_stars.get_num_stars());

	int b;
	for ( b = 1; b < 10; ++b )
	{
		ASTAR_FIELD stars;
		fprintf(stderr, "Red/ Blueshift %d\n", b );
		image2.copy( image, 0, 0, width, height, 0, 0 );
		image2.blueshift( b );
		image2.redshift( -b );
		int stars_found = image2.starfind( .20, stars );
		float ratio, ratio_count;
		
		stars.map_to( orig_stars );
		stars.filter_out_unmapped();

		ratio = stars.get_ratio();
		ratio_count = stars.get_num_stars();
		fprintf(stderr, "%d stars found\n", stars_found );
		fprintf( stderr, "Avg Ratio is %g on %g samples\n", ratio, ratio_count );
		if ( ratio >= best_sample )
		{
			best_sample = ratio;
			best_shift = b;
			best_stars = stars;
			fprintf(stderr, "New best point found\n");
		}
	}

	fprintf( stderr, "Using shift %d \n", best_shift );

	image2.copy( image, 0, 0, width, height, 0, 0 );
	image2.blueshift( best_shift );
	image2.redshift( -best_shift );

#ifdef TODO
	int i;

	for ( i = 0; i < stars_found; ++i )
	{
		APIXEL red, green, white;

		red.r = FULL_INT; red.g = 0; red.b = 0;
		green.r = 0; green.g = FULL_INT; green.b = 0;
		white.r = FULL_INT; white.g = FULL_INT; white.b = FULL_INT;

		image2.circle( stars[ i ].xc, stars[ i ].yc, stars[i].minrad, red ); 
		image2.circle( stars[ i ].xc, stars[ i ].yc, stars[i].maxrad, green); 

		image2.setpixel( stars[i].xc, stars[i].yc, white );

	}
#endif

	//image2.drawstars( best_stars ); 
	//image2.drawstars( orig_stars ); 

	fprintf(stderr, "writing image\n");
	image2.writeppm( "stdout", false );
#endif
}

